import type { LineString, Polygon, Point, Feature, Geometry, GeoJsonProperties } from 'geojson'
import type { FillStyle, LineStyle, SymbolStyle } from '../types'
import type { LngLat } from '@/geom/great-circle'

export interface SymbolProperties {
  text?: string
  iconImage: string
  textColor: string
  textOffSet?: number[]
}

export type SymbolFeature = Feature<Point, SymbolProperties>

export interface LineProperties {
  lineColor: string
  lineWidth: number
  lineOpacity: number
}

export type LineFeature = Feature<LineString | Polygon, LineProperties>

export interface FillProperties {
  color: string
  outlineColor: string
  opacity: number
}

export type FillFeature = Feature<Polygon, FillProperties>

export function symbolProperties(
  text: string | undefined,
  style: SymbolStyle,
  icon: string | undefined = undefined
): SymbolProperties {
  return {
    text,
    iconImage: icon || style.icon || '',
    textColor: style.color,
    textOffSet: style.textOffset || [0.4, 0.4]
  }
}

export function featureWithProperties<G extends Geometry, P extends GeoJsonProperties>(
  f: Feature<G>,
  properties: P
): Feature<G, P> {
  return {
    type: 'Feature',
    geometry: f.geometry,
    properties
  }
}

export function asSymbolFeature(
  feature: Feature<Point>,
  text: string | undefined,
  style: SymbolStyle
): SymbolFeature {
  feature.properties = symbolProperties(text, style)
  return feature as SymbolFeature
}

export function makeSymbolFeature(
  feature: Feature<Point>,
  text: string | undefined,
  style: SymbolStyle
): SymbolFeature {
  const txt = text || feature.properties?.text || undefined
  const icon = style.icon || feature?.properties?.iconImage
  const properties = symbolProperties(txt, style, icon)
  return featureWithProperties(feature, properties)
}

export function lineProperties(style: LineStyle): LineProperties {
  return {
    lineColor: style.lineColor,
    lineWidth: style.lineWidth,
    lineOpacity: style.lineOpacity
  }
}

export function asLineFeature(
  feature: Feature<LineString | Polygon>,
  style: LineStyle
): LineFeature {
  feature.properties = lineProperties(style)
  return feature as LineFeature
}

export function makeLineFeature(
  feature: Feature<LineString | Polygon>,
  style: LineStyle
): LineFeature {
  const properties = lineProperties(style)
  return featureWithProperties(feature, properties)
}

export function fillProperties(style: FillStyle): FillProperties {
  return {
    color: style.fillColor,
    outlineColor: style.fillOutlineColor,
    opacity: style.fillOpacity
  }
}

export function asFillFeature(feature: Feature<Polygon>, style: FillStyle): FillFeature {
  feature.properties = fillProperties(style)
  return feature as FillFeature
}

export function makeFillFeature(feature: Feature<Polygon>, style: FillStyle): FillFeature {
  const properties = fillProperties(style)
  return featureWithProperties(feature, properties)
}

export interface MapFeatures {
  symbol?: SymbolFeature[]
  line?: LineFeature[]
  fill?: FillFeature[]
}

export interface MapFeatureStyles {
  symbol: SymbolStyle
  line: LineStyle
  fill: FillStyle
}

export function changeMapFeaturesStyle(mf: MapFeatures, styles: MapFeatureStyles): MapFeatures {
  const symbol = mf.symbol?.map((e) => makeSymbolFeature(e, undefined, styles.symbol))
  const lp = lineProperties(styles.line)
  const line = mf.line?.map((e) => featureWithProperties(e, lp))
  const fp = fillProperties(styles.fill)
  const fill = mf.fill?.map((e) => featureWithProperties(e, fp))
  return { symbol, line, fill }
}

export function appendMapFeatures(target: MapFeatures, that: MapFeatures) {
  if (that.symbol) {
    if (!target.symbol) {
      target.symbol = []
    }
    target.symbol.push(...that.symbol)
  }
  if (that.line) {
    if (!target.line) {
      target.line = []
    }
    target.line.push(...that.line)
  }
  if (that.fill) {
    if (!target.fill) {
      target.fill = []
    }
    target.fill.push(...that.fill)
  }
}

export function mergeMapFeatures(fs: MapFeatures[]): MapFeatures {
  const symbol: SymbolFeature[] = []
  const line: LineFeature[] = []
  const fill: FillFeature[] = []
  for (const f of fs) {
    if (f.symbol) {
      symbol.push(...f.symbol)
    }
    if (f.line) {
      line.push(...f.line)
    }
    if (f.fill) {
      fill.push(...f.fill)
    }
  }

  return { symbol, line, fill }
}

export function centerOfMapFeatures(fs: MapFeatures): LngLat | null {
  let lng = 0
  let lat = 0
  let cnt = 0

  if (fs.symbol) {
    for (const f of fs.symbol) {
      const coord = f.geometry.coordinates
      lng += coord[0]
      lat += coord[1]
      cnt += 1
    }
  }
  if (fs.line) {
    for (const f of fs.line) {
      const type = f.geometry.type
      if (type === 'LineString') {
        for (const coord of f.geometry.coordinates) {
          lng += coord[0]
          lat += coord[1]
          cnt += 1
        }
      } else {
        const f1 = f as Feature<Polygon>
        if (f1.geometry.coordinates.length > 0) {
          for (const coord of f1.geometry.coordinates[0]) {
            lng += coord[0]
            lat += coord[1]
            cnt += 1
          }
        }
      }
    }
  }
  if (fs.fill) {
    for (const f of fs.fill) {
      if (f.geometry.coordinates.length > 0) {
        for (const coord of f.geometry.coordinates[0]) {
          lng += coord[0]
          lat += coord[1]
          cnt += 1
        }
      }
    }
  }

  if (cnt === 0) {
    return null
  }
  lng /= cnt
  lat /= cnt
  return { lng, lat }
}
